
import java.awt.Color;
import javax.swing.BoxLayout;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.swing.*;

// TODO Implement image playback and lerp drawing.
// TODO Implement image tools.
// TODO Implement proper image sizing.
// TODO Implement proper game logic. (Currently just keeps passing off the drawing between users.)
public class Game extends JPanel {

	private ImagePanel imagePanel;
	private ColorPalette colorPalette;
	private GuessPanel guessPanel;
	private static final long serialVersionUID = 1L;
	private DatabaseConnection dbConnect;
	private Controller controls;
	private String localPlayer;
	private String remotePlayer;
	private boolean firstPlayer;
	private String word;
	private String state;
	private int value;

	/**
	 * Constructor for Application Panel
	 */
	public Game(Controller control, DatabaseConnection db) {
		controls = control;
		dbConnect = db;
		state = "draw";
		word = "";
		// TODO: Get the state of the game at this point
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

		imagePanel = new ImagePanel(new Image());
		colorPalette = new ColorPalette(controls, imagePanel);
		guessPanel = new GuessPanel(this.controls, this, dbConnect);
		add(colorPalette);
		add(imagePanel);
		add(guessPanel);
		this.setBackground(new Color(249, 249, 252));
	}
        
        
        /**
            Clears the image that this game has 
        **/
        public void clearImage() {
            this.imagePanel.clearImage();
        }
        

	/**
	 * Start a new game.
	 *
	 * @param localName username of the local user starting the game
	 * @param remoteName username of the remote user being challenged
	 */
	public void create(String localName, String remoteName) {
            // smithre5: Assuming that user can have a game with opponent.
            String createGameState = String.format(
                "INSERT INTO game_state VALUES('%s', '%s', '%s', '%s', null, null)",
                localName, remoteName, "select", "1"
            );

            dbConnect.connect();
            int res = dbConnect.executeUpdate(createGameState);
            dbConnect.closeConnection();

            if (res > -1) {
                this.firstPlayer = true;
                this.localPlayer = localName;
                this.remotePlayer = remoteName;
                this.imagePanel.setDrawing(true);
                this.guessPanel.setState("draw");
                this.colorPalette.setDrawing(true);
            } 
            else { // Game wasn't added.
                System.err.println("Failed to create game in DB.");
            }
	}

	public void loadImage(String word) {
		String getGameEvents = String.format(
                    "SELECT br_x AS x, br_y AS y, br_color AS color, br_size AS size, `order` AS `order` "
                        + "FROM game_events WHERE user1='%s' AND user2='%s' ORDER BY `order` ASC",
                    firstPlayer ? localPlayer : remotePlayer,
                    firstPlayer ? remotePlayer : localPlayer
                );

		dbConnect.connect();

		ResultSet gameEvents = dbConnect.executeQuery(getGameEvents);

		if (gameEvents != null) {
                    try {
                        ArrayList<ImagePoint> picture = new ArrayList<ImagePoint>();
                        while (gameEvents.next()) {

                                Point p = new Point(gameEvents.getInt("x"), gameEvents.getInt("y"));
                                int color = gameEvents.getInt("color");
                                int size = gameEvents.getInt("size");
                                int order = gameEvents.getInt("order");
                                ImagePoint newInfo = new ImagePoint(color, order, size, p);
                                picture.add(newInfo);
                        }

                        this.guessPanel.setState("guess");
                        this.imagePanel.setDrawing(false);
                        this.colorPalette.setDrawing(false);
                        this.clearImage();
                        this.imagePanel.drawImage(picture);
                    } 
                    catch (SQLException e) {
                            // TODO Handle failure.
                        System.err.println("ERR: Game.loadImage: failed to get event: " + e.getMessage());
                    }
		}

		dbConnect.closeConnection();
	}

	
	public void setDrawing(boolean state) {
		colorPalette.setDrawing(state);
		imagePanel.setDrawing(state);
	}
	
	
	/**
	 * Gets the word of this game
        *
	 */
	public String getWord() {
		return word;
	}

	/**
	 * Gets the state of this game
        *
	 */
	public String getState() {
		return state;
	}

	/**
	 * Sets the word of this game
        *
	 */
	public void setWord(String word) {
		this.word = word;
	}

	/**
	 * Sets the word of this game
        *
	 */
	public void setState(String state) {
		this.state = state;
	}
	
	
	/**
	 * Returns guess panel (brittle, but time is a factor)
	 */
	public GuessPanel getGuessPanel() {
		return guessPanel;
	}
	
	public ColorPalette getPalette() {
		return colorPalette;
	}
	
	/**
	 * Gets the current image instance 
        *
	 */
	public Image getImage() {
		return this.imagePanel.getImage();
	}

	public void setValue(int v) {
		// Clamp value to [0, 3]
		value = Math.max(0, Math.min(3, v));
	}
        
	/**
	 * Gets the coin value for the word.
	 */
	public int getValue() {
		return value;
	}

	/**
	 * Gets the local player 
        *
	 */
	public String getLocalPlayer() {
		return this.localPlayer;
	}

	/**
	 * Sets the local player 
        *
	 */
	public void setLocalPlayer(String localPlayer) {
		this.localPlayer = localPlayer;
	}
        
        /**
            Sets the remote player 
        **/
        public void setRemotePlayer(String remotePlayer) {
            this.remotePlayer = remotePlayer;
        }
        
        /**
            Gets the remote player 
        **/
        public String getRemotePlayer() {
            return this.remotePlayer;
        }
        
        /**
            Gets whether this player is the first player 
        **/
        public boolean getFirstPlayer() {
            return this.firstPlayer;
        }
        
        /**
            Sets the firstPlayer variable 
        **/
        public void setFirstPlayer(boolean firstPlayer) {
            this.firstPlayer = firstPlayer;
        }
        
        
        /**
         * Assigns game word and advances play state.
         * 
         * @param word
         */
        public void selectWord(String word) {
            if (localPlayer == null || remotePlayer == null) {
                return;
            }
            
            // smithre5: This should check that the word is valid, ie in DB
            // and one of the options available. But I'm lazy. Also assumes
            // ...forgot where I was going with that.
            
            String selectGameWord = String.format(
                    "UPDATE game_state SET state='%s', word='%s' WHERE user1='%s' AND user2='%s'",
                    "draw", word, localPlayer, remotePlayer
            );
            
            
            dbConnect.connect();
            int res = dbConnect.executeUpdate(selectGameWord);
            dbConnect.closeConnection();
            
            if (res > -1) {
                this.word = word;
                this.guessPanel.setWord(word);
                this.clearImage();
                this.colorPalette.setDrawing(true);
                this.imagePanel.setDrawing(true);
                this.guessPanel.setState("draw");
                this.controls.openGame(remotePlayer);
            }
            else {
                // TODO Handle failure.
                System.err.println("Failed to set game word.");
            }
        }

	/**
	 * Assigns game word and advances play state.
	 *
	 * @param word
	 */
	public void selectWord(String word, int value) {
		if (localPlayer == null || remotePlayer == null) {
			return;
		}

		// smithre5: This should check that the word is valid, ie in DB
		// and one of the options available. But I'm lazy. Also assumes
		// ...forgot where I was going with that.

		String selectGameWord = String.format(
                    "UPDATE game_state SET state='%s', word='%s', value=%d WHERE user1='%s' AND user2='%s'",
                    "draw", word, value, firstPlayer ? localPlayer : remotePlayer, firstPlayer ? remotePlayer : localPlayer
                );


		dbConnect.connect();
		int res = dbConnect.executeUpdate(selectGameWord);
		dbConnect.closeConnection();

		if (res > -1) {
                    this.word = word;
                    this.clearImage();
                    this.value = value;
                    this.guessPanel.setWord(word);
                    this.guessPanel.setState("guess");
                    this.colorPalette.setDrawing(true);
                    this.imagePanel.setDrawing(true);
                    this.controls.openGame(remotePlayer);
		} 
                else {
                    // TODO Handle failure.
                    System.err.println("Failed to set game word.");
		}
	}

	/**
	 * Compares normalized guess word to game word and assigns win or loss.
	 *
	 * @param normalizedGuess guess word, trimmed and in lowercase
	 * @return true if win; false otherwise
	 */
	public boolean checkGuess(String normalizedGuess) {
		// TODO Error checking, maybe?

		boolean win = false;
		dbConnect.connect();

		String removeEventsFmt =
				"DELETE FROM game_events WHERE user1='%s' AND user2='%s'";

		System.out.println(word);
		if (normalizedGuess.equals(word)) { // win
			// Update stats				
			String winStatsFmt =
					"UPDATE user_stats SET wins = wins + 1 WHERE username='%s'";

			dbConnect.executeUpdate(String.format(winStatsFmt, localPlayer));
			dbConnect.executeUpdate(String.format(winStatsFmt, remotePlayer));

			// Add coins (yay, coins)
			String winCoinsFmt =
					"UPDATE user SET coins = coins + %d WHERE username='%s'";

			dbConnect.executeUpdate(String.format(winCoinsFmt, value, localPlayer));
			dbConnect.executeUpdate(String.format(winCoinsFmt, value, remotePlayer));

			// Remove game events				
			dbConnect.executeUpdate(
                            String.format(
                                removeEventsFmt,
                                firstPlayer ? localPlayer : remotePlayer,
                                firstPlayer ? remotePlayer : localPlayer
                            )
                        );

			// Advance state
			dbConnect.executeUpdate(
                            String.format(
                                "UPDATE game_state SET state='select', player=%d WHERE user1='%s' AND user2='%s'",
                                firstPlayer ? 1 : 2,
                                firstPlayer ? localPlayer : remotePlayer,
                                firstPlayer ? remotePlayer : localPlayer
                            )
                        );
                        
                        System.out.println(String.format(
                                "UPDATE game_state SET state='select', player=%d WHERE user1='%s' AND user2='%s'",
                                firstPlayer ? 1 : 2,
                                firstPlayer ? localPlayer : remotePlayer,
                                firstPlayer ? remotePlayer : localPlayer
                            ));

			win = true;
                        this.clearImage();
		} else { // lose
			// Update stats
			String loseStatsFmt =
					"UPDATE user_stats SET losses = losses + 1 WHERE username='%s'";

			dbConnect.executeUpdate(String.format(loseStatsFmt, localPlayer));
			dbConnect.executeUpdate(String.format(loseStatsFmt, remotePlayer));

			// Remove game events				
			dbConnect.executeUpdate(String.format(
					removeEventsFmt,
					firstPlayer ? localPlayer : remotePlayer,
					firstPlayer ? remotePlayer : localPlayer));

			// Remove game
			dbConnect.executeUpdate(String.format(
					"DELETE FROM game_state WHERE user1='%s' AND user2='%s'",
					firstPlayer ? localPlayer : remotePlayer,
					firstPlayer ? remotePlayer : localPlayer));
		}

		dbConnect.closeConnection();

		return win;
	}
}